# -*- coding: utf-8 -*-
# 制作本地可执行文件
# pyinstaller -F -w main.py
# pyinstaller -F -i ico.ico -w main.py
import copy
import shutil
import struct
# pip install Nuitka
# nuitka --onefile --windows-disable-console --include-qt-plugins=sensible,styles --standalone --show-memory --show-progress --nofollow-imports --enable-plugin=pyqt5 --follow-import-to=PyQt5 --output-dir=out --windows-icon-from-ico=./icon.ico main.py

import time
import sys
import traceback
import datetime
from time import sleep
import os

from serial import Serial
from main_ui import Ui_MainWindow

# 其他平台需要换
from serial.tools.list_ports_windows import comports

# 引入窗口文件
from PyQt5 import QtWidgets
from PyQt5.QtGui import QIcon, QPixmap
from PyQt5.QtCore import QThread, pyqtSignal, Qt
from PyQt5.QtWidgets import QApplication, QMessageBox, QListWidgetItem
from PyQt5.QtSerialPort import QSerialPortInfo, QSerialPort

# 图标文件
iconB = b'\x00\x00\x01\x00\x01\x00  \x00\x00\x01\x00 \x00\xa8\x10\x00\x00\x16\x00\x00\x00(\x00\x00\x00 \x00\x00\x00@\x00\x00\x00\x01\x00 \x00\x00\x00\x00\x00\x00\x10\x00\x00\xc3\x0e\x00\x00\xc3\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00:Od\x00;Of\x00:Of\x00:Of\x00;Of\x00;Pf\x00;Pf\x00;Pf\x00;Pf\x00:Oe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00;Pe\x00;Pe\x00;Pf\x00;Pf\x00:Of\x02:Of\x01;Of\x00;Pf\x00;Pf\x02;Oe\x01;Pf\x00;Pf\x00;Pe\x00;Pf\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00;Pe\x00;Pe\x04;Pf";PfV:OfH;Pg\t+=P\x00.AU\x00;Pg\x0f;PfP;PfQ;Pf\x1c;Pe\x02;Pe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00;Pf\x00;PfK;Pf\xd0<Qgo<Od\x12\x19\x16\x15\x05\x1d#+\r\x1d")\x0c\x1a\x17\x16\x04=Qf\x18;Qg\x80;Pf\xcc;Pf1;Pf\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 /@\x00 0A\x003FZ\x00=Si\x1d8La\xbc-?R\x94 4I\x89!9R\xb5">Y\xc4"=Y\xc2!9P\xb1 4H\x81/BV\xa29Mb\xab@Vm\x0f5I]\x00!1B\x00(7G\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 /@\x00 .>\x00\x1e\'2\x07\x1f0AY$<U\xd4%Gi\xff&Py\xff&T\x80\xff\'W\x84\xff\'V\x84\xff&T\x7f\xff%Ow\xff%Ff\xff#;R\xc5\x1f/?G\x1e\x1d \x02!/>\x00!1B\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00:Pf\x00;Pf\x00;Pf\x00;Pf\x00;Pf\x00 0A\x00\x1f.>\x00\x1e)6\x0c!4H\x8d%Gi\xf7(W\x85\xff)\\\x8c\xff)\\\x8c\xff)\\\x8c\xff)[\x8b\xff)[\x8b\xff)\\\x8b\xff)\\\x8c\xff)\\\x8c\xff)V\x82\xff&Fd\xef%7In!&-\x03!0?\x00\x1f0@\x00;Pf\x00;Pf\x00;Pf\x00;Pf\x00;Pf\x00\x00\x00\x00\x00\x00\x00\x00\x00;Pf\x00;Pf\x05;Pf5:Pf\x06:Pf\x00\x1f0A\x000c\x9a\x00!3Gu\'Lp\xfd,_\x91\xff,`\x92\xff,_\x91\xff,_\x91\xff,`\x91\xff,`\x91\xff-`\x91\xff.a\x92\xff/b\x93\xff0c\x93\xff1d\x94\xff2c\x92\xff+Kk\xf3 1BS"4H\x00\x161=\x00:Pf\x00;Pf\x0c;Pf:;Pf\x02;Pf\x00\x00\x00\x00\x00\x00\x00\x00\x00;Pf\x00;Pf\r;Pf\x8f;Pf\x17;Of\x00 0A\x00\x1e*6\x1c%B_\xd8.a\x93\xff/e\x98\xff/d\x98\xff-a\x92\xff/c\x95\xff2g\x9a\xff3g\x99\xff4h\x9a\xff5i\x9b\xff3e\x96\xff5g\x97\xff9l\x9d\xff:l\x9d\xff5d\x90\xff$<U\xb9\x1b"*\n 0@\x00;Pf\x00;Pf";Pf\x98;Pf\x07;Pf\x00\x00\x00\x00\x00\x00\x00\x00\x00;Pf\x00;Pf\t;Pf\x9c;Pf#;Pf\x00 0A\x00\x1f,;@)Mp\xf53k\x9f\xff0c\x94\xff$B^\xff3CR\xff-AS\xff(Os\xff9n\xa0\xff7i\x98\xff(Gd\xff4DS\xff1BS\xff,Op\xff@r\xa1\xff<o\xa0\xff\'Ec\xe0\x1d(3\x1d 0A\x00;Pf\x00;Pf9;Pf\xa4;Pf\x06;Pf\x00\x00\x00\x00\x00\x00\x00\x00\x00;Pf\x00;Pf\x06;Pf\xa8;Pf=;Pf\x00 0A\x00\x1f-<G+Ot\xf98o\xa4\xff*D]\xff\x98\x98\x98\xff\xe5\xe5\xe4\xff\xd4\xd3\xd2\xffhlo\xff*Kk\xff-D[\xff\x8f\x90\x90\xff\xe0\xdf\xdf\xff\xdc\xdb\xda\xffknq\xff4Wy\xffBv\xa8\xff(Hg\xe1\x1d\'3\x1e 0A\x00;Pf\x00;PfT;Pf\x9b8Ne\x01:Of\x00\x00\x00\x00\x00;Pf\x00;Pf\x00:Pe\x03;Pf\xa7;Pf^;Pf\x00 0A\x00\x1e,:>-Rx\xf45g\x98\xffT\\d\xff\xfa\xf9\xf8\xff\xf2\xf2\xf2\xffqqq\xff\xad\xad\xad\xff?CG\xffsuv\xff\x8d\x8d\x8c\xff\x98\x98\x98\xff\xff\xff\xff\xff\xd6\xd5\xd4\xff6HZ\xffBv\xa9\xff(Gf\xdf\x1c&2\x1d 0A\x00;Pf\x00;Pfz;Pf\x86;Pf\x00:Qd\x00:Rd\x009Oe\x00;Pf\x00;Pf\x00;Pf\x8b;Pf\x87;Pf\x00 0A\x00\x1d(4"+Np\xe26h\x98\xff^dk\xff\xfd\xfc\xfc\xff\xf3\xf3\xf3\xff\x86\x86\x86\xff\xce\xce\xce\xff||{\xff\xb0\xb0\xb0\xff\xa6\xa6\xa6\xff\xa8\xa8\xa8\xff\xff\xff\xff\xff\xde\xdd\xdd\xff:JZ\xffAu\xa7\xff\'C`\xc5\x19\x1e%\r#4F\x009Nd\x01;Pf\xa1;Pfv;Pf\x00;Lh\x00?At\x00;Pf];Pf\x13;Pf\x00;Pfp;Pf\xb1:Oe\x05*;N\x00\x17\x19\x1e\x08&A[\xb5;o\xa1\xff;M_\xff\xd3\xd1\xd0\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfc\xfc\xfc\xffYYY\xff\x96\x96\x96\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xa0\xa0\xa0\xff:Xv\xff<m\x9d\xff#8O\x8eE\x92\xd8\x009Mc\x00;Pf\x0f;Pf\xc4;PfR;Pf\x00;Pf\x1e;PfN;Pf\x97;PfJ;Pf\x00;PfH;Pf\xd1;Pf\x1b;Pf\x00#7L\x00 2D`5`\x8b\xfc=j\x96\xffNW`\xff\xb9\xb8\xb8\xff\xd9\xd8\xd7\xff\x8a\x8b\x8c\xff2H]\xffAP]\xff\xaf\xae\xae\xff\xda\xd9\xd8\xff\x9c\x9c\x9c\xffARb\xffJ}\xaf\xff/Uz\xef\x1f-<= 1B\x00;Pf\x00;Pf-;Pf\xd6;Pf1;Pf\x00;Pfb;Pf\x80;Pf`;Pf\x99:Od\x02;Pf#;Pf\xd9;PfE;Pf\x00\x1f/?\x00\x1b$.\x10\'A[\xb6Bw\xab\xffK{\xa9\xff?[v\xffDZp\xffCd\x85\xff_\x91\xc2\xffX\x86\xb3\xffB]x\xffF\\q\xffEd\x82\xffW\x8a\xbb\xff=m\x9c\xff#:P\x93\x15\x16\x18\x04\x1f.?\x00;Pf\x00;Pf_;Pf\xce;Oe\x13:Pf\x0b;Pf\xa8;PfF;Pf\x1a;Pf\xbb;PfD;Pf\x05;Pf\xbc;Pf\x88;Pf\x00;Pf\x00/BV\x00\x1c*8-*Ih\xd1H~\xb3\xffb\x9a\xd1\xffh\x9c\xd0\xffk\x9f\xd3\xffk\x9e\xd1\xffl\x9f\xd3\xffm\xa1\xd3\xffl\x9f\xd2\xffe\x9c\xd2\xffCt\xa5\xff&@Z\xb9\x1a$1\x181DX\x00;Pf\x00;Oj\x00;Pf\xa3;Pf\xa3<Pg\x00;Pf\\;Pf\xaf;Pf\r;Pf\x00;Pfp;Pf\xb9;Pf\x0f;Pft;Pf\xdf;Pfi;PfK;Pf=>Tj!$5Fp\'C_\xfd>k\x99\xffX\x8e\xc4\xffg\x9d\xd4\xffl\xa2\xd7\xffm\xa2\xd8\xffg\x9d\xd3\xffV\x8b\xbf\xff9d\x8f\xff%>X\xf2\'7HT>Tj$;Pf@;PfL;Pft;Pf\xe5;PfW;Pf\x1c;Pf\xc7;PfW;Pf\x00;Pf\x00;Pe\x14;Pf\xbf;Pf\x87;Pf\x1e;Pf\xa4;Pf\xd9;Pf\xdf;Pf\xe5;Qg\xdd2FZ\xe4(Jk\xfe+Sz\xff-Ps\xff6]\x85\xff;d\x8d\xff;d\x8d\xff5[\x82\xff/Rt\xff-V~\xff(Ge\xfb6J_\xe0;Pf\xdf;Pf\xe4;Pf\xdf;Pf\xd7;Pf\x96:Pf\x1b;Pf\xa0;Pf\xac;Pe\n;Pf\x00;Pe\x00;Pf\x00;PfB;Pf\xdd;Pfj;Pe\x06;Pf\x16;Pf\';PfJ<Qgj3EY\xa2+Mn\xf88p\xa6\xff<p\xa2\xff;j\x98\xff:d\x8e\xff<f\x90\xff@o\x9b\xffEw\xa7\xff9o\xa3\xff)Ea\xf07J^\x96;Pff;PfD;Pf$;Pf\x14:Oe\x08;Pf\x80;Pf\xd6;Pf.;Pf\x00;Pf\x00;Qf\x00;Of\x00=Uf\x00;Pfh;Pf\xe4;Pf\x97;Pf\x88;Pf\xac;Pf\xc6;Pf\xd78La\xea*B[\xfe:p\xa4\xffK\x82\xb7\xffP\x85\xb9\xffR\x87\xba\xffT\x88\xba\xffV\x89\xbb\xffP\x86\xb9\xff6h\x97\xff+?U\xfd;Od\xe6;Pf\xd5;Pf\xc3;Pf\xa8;Pf\x85;Pf\xa0;Pf\xe0;PfP;Pf\x00;Pe\x00:Pe\x00;Pf\x00;Pf\x00;Pf\x00;Pf\x02;Pff;Pf\xc1;Pf\xbb;Pf\x9e;Pf~1CW\x8f(?V\xe9$@\\\xff0X\x80\xffK\x85\xbe\xff]\x92\xc6\xffb\x95\xc6\xffd\x96\xc7\xffa\x94\xc7\xffJ\x82\xb9\xff/Ru\xff)D_\xff.CY\xe04G[\x88;Pf\x81;Pf\xa3;Pf\xbd;Pf\xbd;PfU7K^\x00;Pe\x00;Pf\x00;Pf\x00;Pf\x0c:Of\x0f;Pf\x00;Pf\x00?Of\x00;Pf\t;Pf\x06\x1d,<\x00\x1e-=8#?[\xd7)V\x82\xff*\\\x8c\xff\'Jl\xff6b\x8e\xffP\x8a\xc4\xffa\x99\xd0\xffb\x99\xd0\xffQ\x89\xbf\xff8_\x86\xff0Tw\xff4d\x93\xff2\\\x84\xff%?Y\xca\x1c+:)\x01\r\x18\x00;Pf\x07;Pf\x08;Pe\x00;Pe\x00;Pf\x00;Pf\x0f;Pf\x07;Pf5;Pf\x88;Pf\x0b;Pf\x00:Pf\x00:Pe\x00*<O\x00\x1a\x1e%\x07#;S\xb0-\\\x8a\xff1f\x99\xff2f\x99\xff2d\x95\xff/Ty\xff5X|\xff=f\x8f\xff>f\x8e\xff8Z{\xff7]\x82\xff<m\x9d\xff>p\x9f\xff>p\xa0\xff-Z\x85\xff"7M\x98\x01\x00\x00\x013F[\x00:Pf\x00;Qg\x00;Pf\x00;Pf\x15;Pf\x8a:Pf\x1e;Pe\x07;Pf\x96;Pf~:Oe\x04;Pf\x00;Pf\x00%6H\x00\x19",\x18&C`\xdb4j\x9e\xff:o\xa3\xff;p\xa4\xff=r\xa5\xff>r\xa5\xff=m\x9b\xff<f\x8f\xff>h\x91\xffBq\x9f\xffEw\xa8\xffFx\xa9\xffHy\xa9\xffFy\xa9\xff4h\x9a\xff%A]\xb8\x00\x00\x00\x03*<O\x00;Pf\x00;Pf\x00:Pf\x0b;Pf\x95;Pf\x81:Pg\x01;Pf\x00;Pf%;Pf\xc4;Pf\x83;Pf\x0b;Pf\x00Nh\x7f\x021BTN)Fd\xed8p\xa6\xffBy\xad\xffEz\xae\xffF{\xaf\xffH|\xaf\xffI~\xb0\xffJ\x7f\xb2\xffL\x80\xb2\xffM\x80\xb2\xffN\x81\xb2\xffP\x82\xb3\xffQ\x83\xb3\xffN\x81\xb2\xff8m\xa1\xff)C^\xd77I\\8Tq\x8c\x00;Pf\x00;Pf\x12;Pf\x98;Pf\xb8;Pf\x17;Pf\x00<Tg\x00;Pf\x00;Pf<;Pf\xd3;Pf\xa8;PfK;Pf\x9a:Nc\xe6+D^\xf29p\xa6\xffI\x81\xb7\xffN\x84\xb8\xffO\x85\xb9\xffQ\x86\xb9\xffR\x87\xba\xffS\x88\xba\xffU\x89\xbb\xffV\x8a\xbb\xffW\x8a\xbc\xffY\x8b\xbc\xff[\x8c\xbd\xffO\x85\xb9\xff7k\x9d\xff,BY\xf1;Oe\xe2;Pf\x8b;PfL;Pf\xb8;Pf\xc2;Pf\';Pf\x00;Pf\x00:Of\x00:Pe\x00;Of\x00;Pf1;Pf\xb8;Pf\xec;Pf\xc7<QgZ$6Is3`\x8b\xfdG\x84\xbe\xffV\x8d\xc2\xffY\x8e\xc3\xffZ\x8f\xc3\xff[\x90\xc3\xff\\\x91\xc4\xff]\x91\xc4\xff_\x92\xc5\xff`\x93\xc5\xffb\x95\xc6\xff_\x93\xc5\xffG\x83\xbc\xff0X\x80\xf6%6H`<Qgg;Pf\xd0;Pf\xeb;Pf\xaa;Pf";Pf\x00<Qh\x00;Of\x00\x00\x00\x00\x00;Pe\x00:Rd\x00;Pf\x00;Pf\x11;Pf;;Pf\x143G[\x00\x1a$/\x11\'A[\xb6<q\xa4\xffP\x8d\xc8\xff_\x96\xcc\xffc\x98\xcd\xffd\x99\xcd\xffe\x9a\xce\xffg\x9b\xce\xffh\x9b\xce\xffi\x9c\xcf\xfff\x9a\xce\xffQ\x8d\xc8\xff9k\x9c\xff%<T\x9d\x13\x18\x1f\x075I_\x00;Pf\x1a;Pf:;Pf\x0b;Pf\x00;Qg\x00:Od\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00;Qe\x00;Pf\x00;Pf\x00;Pf\x00;Pf\x00%6H\x00 /@\x00\x1e*9$(C^\xbb;m\x9e\xffO\x8c\xc9\xff_\x9a\xd4\xffh\x9f\xd6\xffk\xa1\xd6\xffl\xa2\xd7\xffj\xa1\xd7\xffa\x9b\xd4\xffN\x89\xc5\xff8f\x94\xfd\'?X\xa7\x1d(5\x19\x1f.>\x00\x00\x00\x00\x00;Pf\x00;Pf\x00;Pf\x00;Pf\x00;Pf\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1f0A\x00"4G\x00 0@\x00\x1c\'3\x18$8N\x81/Rv\xda<l\x9c\xfaF~\xb5\xffM\x88\xc1\xffM\x87\xc0\xffE{\xb1\xff:h\x96\xf8-No\xd1"5Io\x1a#.\x0e\x1f.>\x00 0B\x00\x1f0A\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 0A\x00 0A\x00\x1f.>\x00\x8b\xff\xff\x00\x1b%1% 1Cf%<T\x96(B\\\xb2(B\\\xaf$:Q\x91 0@\\\x1a#.\x1d+Lj\x00\x1f.>\x00 0A\x00 0A\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xe0\x07\xff\xff\x80\x01\xff\xff\x80\x01\xff\xff\x80\x01\xff\xfe\x00\x00\x7f\xfe\x00\x00\x7f\x80\x00\x00\x01\x80\x00\x00\x01\x80\x00\x00\x01\x80\x00\x00\x01\x80\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x00\x00\x01\xc0\x00\x00\x03\xfe\x00\x00\x7f\xff\x00\x00\xff'

# 解决不同电脑不同缩放比例问题
# QGuiApplication.setAttribute(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
######使用下面的方式一定程度上可以解决界面模糊问题--解决电脑缩放比例问题
QApplication.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
# 适应高DPI设备
QApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
# 解决图片在不同分辨率显示模糊问题
QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps)

DEBUG = True
mainTitle = "MD-STC 编程器"
# 编程器返回版本信息
infoName = "MD-STC"

main_W = 1050
main_H = 590

# 临时缓存文件
temp_file = "./_temp.bin"
writeFileLength = 0x1000000

def dprint(*args, **kwargs):
    stack = traceback.extract_stack()
    stack = stack[len(stack) - 2]
    msg = "[{:s}] [{:s}:{:d}] {:s}(): {:s}".format(
        datetime.datetime.now().astimezone().replace(microsecond=0).isoformat(),
        stack.filename[stack.filename.replace("\\", "/").rindex("/") + 1:], stack.lineno, stack.name,
        " ".join(map(str, args)), **kwargs)
    if DEBUG:
        msg = "{:s}{:s}".format('\033[2K', msg)
        print(msg)


class CMD:
    Info = 0x02
    Version = 0x03
    Test = 0x04

    Read = 0xA1
    Write = 0xB1
    Erase = 0xC1

    Lock = 0xD1
    Unlock = 0xD2

    # 设置地址
    SetAddress = 0xE1
    # 设置缓存
    SetBuffer = 0xE2
    # 设置写入字节长度 2字节 1字节、64、128、256、512
    SetWriteBuffer = 0xE3
    # 设置读写16字节顺序 MD默认1 高位+低位。8位模式0 低位+高位
    SetMdMode = 0xE4

class Programmer(Serial):
    def __init__(self):
        super().__init__(baudrate=1000000, timeout=2, write_timeout=2)
        # super().__init__(baudrate=115200, timeout=2, write_timeout=2)
        # self.identify()

    def connect(self, comport):
        for p in comports():
            if p.device == comport:
                self.port = p.device
                try:
                    self.open()
                    if self.is_open:
                        if self.confirmation():
                            return True
                except:
                    continue
        return False

    def testConnect(self, comport):
        for p in comports():
            if p.device == comport:
                # 设置驱动设备 COMX
                self.port = p.device
                try:
                    self.open()
                except:
                    continue
                try:
                    if self.is_open:
                        if self.confirmation():
                            self.sendCMD(CMD.Info)
                            data = self.getline()
                            print('{}:{}'.format(infoName, data))
                        else:
                            print("Not ready")
                            return -1
                    else:
                        print('Port not open')
                        return -1
                except:
                    self.close()
                    return -1

                # 判断返回值是否是 EEPROM_STC_V2
                if data == infoName:
                    self.sendCMD(CMD.Version)
                    data = self.readline()
                    print("version:{}".format(data))
                    # 正常返回
                    return 1
                else:
                    self.close()
                    # 版本不匹配
                    return -9999
            else:
                print("没有找到 {}".format(comport))
        return -9

    '''
    # 1
    buffer.extend(struct.pack("B", 1))
    # 2
    buffer.extend(struct.pack("H", 2))
    # 4
    buffer.extend(struct.pack("I", 4))
    '''

    def sendCMD(self, cmd, length=0, data=0):
        buffer = bytearray()
        buffer.extend(struct.pack("B", cmd))
        if length == 1:
            buffer.extend(struct.pack("B", data))
        elif length == 2:
            buffer.extend(struct.pack(">H", data))
        elif length == 4:
            buffer.extend(struct.pack(">I", data))
        self.write(buffer)
        self.flush()

    def getline(self):
        # return self.readline().decode().rstrip('\r\n')
        try:
            res = self.readline().decode()
        except Exception as err:
            print("--------------Exception------------")
            print(err)
            traceback.print_exc()
            print("--------------Exception------------")

        if res.endswith("\r\n") or res.endswith("\n\r"):
            res = res[0:len(res) - 2]
        elif res.endswith("\n"):
            res = res[0:len(res) - 1]
        elif res.endswith("\r"):
            res = res[0:len(res) - 1]
        return res

    def confirmation(self):
        self.sendCMD(CMD.Test)
        return (self.getline().startswith('ok'))

    def getversion(self):
        self.sendCMD(CMD.Version)
        version = self.getline()
        return version


class ChipThread(QThread):

    # mode r读取 w写入 c校验 s保存 ff查空
    def __init__(self, port, chip, chipName, startAddress, endAddress, mode='r', file=''):
        super(ChipThread, self).__init__()
        self.chip = chip
        self.port = port
        self.startAddress = startAddress
        self.endAddress = endAddress
        self.mode = mode
        self.file = file
        self.chipName = chipName
        # 读取芯片内容
        # self.chipData = []

    # 信号槽
    browserClear_signal = pyqtSignal()  # 清理日志框
    textBrowserAppend_signal = pyqtSignal(str)  # 追加日志
    hexBrowserAppend_signal = pyqtSignal(str)  # 追加hex框
    progressBarChange_signal = pyqtSignal(int)  # 进度条更新

    def appendMsg(self, msg):
        self.textBrowserAppend_signal.emit(msg)

    def appendHexMsg(self, msg):
        self.hexBrowserAppend_signal.emit(msg)

    def progressBarChange(self, value):
        self.progressBarChange_signal.emit(value)

    def run(self):
        try:
            self.isHigh = 0
            if self.mode == 'w':
                # 写片
                self.write()
            elif self.mode == 'c':
                self.read()
                self.check()
            elif self.mode == 'ff':
                self.read()
            elif self.mode == 'r':
                self.read()
            elif self.mode == 's':
                self.read()
                self.save()
            else:
                # 读片
                self.read()

        except Exception as e:
            print("错误")
            print(e)
            traceback.print_exc()

    def write(self):
        totalCount = 0
        # 缓存 128
        buffer_size = 128
        eeprom = Programmer()
        if eeprom.connect(self.port):
            print("连接")
        else:
            print("未连接")
            self.appendMsg("==编程器未连接，或端口被占用==")
            return
        if eeprom.is_open:
            datalength = filelength = os.path.getsize(self.file)

            # 设置模式
            eeprom.sendCMD(CMD.SetMdMode, 4, self.chip['mdmode'])
            dprint("设置MD模式({}): {}".format(self.chip['mdmode'], eeprom.readline()))

            if 'buff' in self.chip:
                # 缓存 128 256
                buffer_size = self.chip['buff']
                dprint("使用写入缓存长度: {}".format(buffer_size))
                eeprom.sendCMD(CMD.SetBuffer, 4, buffer_size)
                dprint("设置缓存长度({}): {}".format(buffer_size, eeprom.readline()))
            # 写入缓存 1 128
            eeprom.sendCMD(CMD.SetWriteBuffer, 4, self.chip['page'])
            dprint("设置写入模式(1,128): {}".format(self.chip['page'], eeprom.readline()))
            # 设置地址开始位置0
            address = 0
            eeprom.sendCMD(CMD.SetAddress, 4, 0)
            dprint("地址({}):{}".format(0, eeprom.readline()))


            # 写入是否出错
            write_error = False
            startTime = time.time()
            with open(self.file, 'rb') as f:
                while datalength > 0 and not write_error:
                    # 读取缓存长度的数据
                    buffer_data = f.read(buffer_size)

                    end = address + buffer_size - 1
                    self.appendMsg('{}-{}'.format(('%08X' % address), ('%08X' % end)))

                    # eeprom.reset_input_buffer()
                    # eeprom.reset_output_buffer()

                    # 写入 传递开始地址
                    eeprom.sendCMD(CMD.Write)
                    print("写地址: {}".format(('%08X' % address)))

                    # 手推车拆包发送
                    if buffer_size == 256:
                        eeprom.write(buffer_data[0:64])
                        print("re 1/4:{}".format(eeprom.getline()))
                        eeprom.write(buffer_data[64:128])
                        print("re 2/4:{}".format(eeprom.getline()))
                        eeprom.write(buffer_data[128:192])
                        print("re 3/4:{}".format(eeprom.getline()))
                        eeprom.write(buffer_data[192:255])
                        print("re 4/4:{}".format(eeprom.getline()))
                    elif buffer_size == 128:
                        eeprom.write(buffer_data[0:64])
                        print("re 1/2:{}".format(eeprom.getline()))
                        eeprom.write(buffer_data[64:128])
                        print("re 2/2:{}".format(eeprom.getline()))
                    else:
                        dprint("==== 不应该走到这里发送数据 ====")
                        eeprom.write(buffer_data)

                    address += buffer_size
                    datalength -= buffer_size
                    totalCount += buffer_size

                    self.progressBarChange(int(totalCount * 100 / filelength))

                    line = eeprom.getline()
                    # 有错就跳出
                    # dprint("line:{}".format(line))

                    if line and (line.find("error") > -1 or len(line) > 10):
                        # 出错返回错误信息及 Ready
                        write_error = True
                        print("写入异常({}) {}".format(len(line), line))
                        self.appendMsg("写入异常 {}".format(line))


            print("写入完成")
            self.appendMsg("写入完成")

            endTime = time.time()
            self.appendMsg(str("写入耗时: {}".format('%.2f' % (endTime - startTime))))

            eeprom.close()

            # sleep(0.5)
            # 取消自动校验功能

            # 没有错误才进行校验
            # if not write_error:
            #     self.mode = 'c'
            #     self.appendMsg("开始校验")
            #     self.read()
            #     self.check()
        else:
            self.appendMsg("编程器连接异常")

    def read(self):
        try:
            self.browserClear_signal.emit()
            print("读取开始")
            eeprom = Programmer()
            sleep(0.1)
            if eeprom.connect(self.port):
                print("连接")
            else:
                print("未连接")
                self.appendMsg("COM连接失败")
            sleep(1)
            if eeprom.is_open:

                # 读取固定使用 64
                read_buff = 64

                # 设置模式
                eeprom.sendCMD(CMD.SetMdMode, 4, self.chip['mdmode'])
                dprint("设置MD模式({}): {}".format(self.chip['mdmode'], eeprom.readline()))

                # 设置地址开始位置0
                eeprom.sendCMD(CMD.SetAddress, 4, 0)
                print("地址:{} {}".format(0, eeprom.readline()))

                startTime = time.time()

                # self.chipData = []
                hex16 = []
                str16 = []
                chr16 = []
                notFFcount = 0
                self.address = 0

                if eeprom.confirmation():
                    print("测试连接成功")
                else:
                    print("测试失败")
                    exit()

                print("---- 准备读取数据 ----")
                if eeprom.confirmation():
                    dprint("==测试连接==")

                # 临时用百分比
                progress_temp = 0
                # 8k缓存
                buff_8k = []

                # 缓存文件
                with open(temp_file, 'wb') as f:

                    self.appendMsg("使用缓存文件: {}".format(temp_file))

                    # 没有到结束地址
                    while self.address < self.endAddress:

                        # 读取指定长度的数据
                        eeprom.sendCMD(CMD.Read)

                        bs = eeprom.read(read_buff)

                        # 写入本地缓存文件
                        f.write(bs)
                        f.flush()

                        if len(bs) < read_buff:
                            dprint("[异常] 读取数据长度: {}".format(len(bs)))
                            self.appendMsg("[异常] 读取数据长度: {}".format(len(bs)))
                            if len(bs) > 0:
                                dprint("[异常] 读取数据内容: {}".format(bs))
                                self.appendMsg("[异常] 读取数据内容: {}".format(bs))
                            eeprom.close()
                            return


                        # 获取缓存长度的数据
                        for i in range(read_buff):
                            b = bs[i]

                            # 写入缓存文件
                            # f.write(struct.pack("B", b))

                            # 查空模式
                            if self.mode == 'ff' and b != 0xff:
                                msg = '%08X' % (self.address + len(hex16))
                                msg += ' = '
                                msg += '%02X' % b
                                self.appendMsg(str(msg))
                                notFFcount += 1

                            hex16.append(b)
                            str16.append('%02X' % b)
                            if len(hex16) > 15:
                                for b16 in hex16:
                                    if (b16 > 31) and b16 < 127:
                                        chr16.append(chr(b16))
                                    else:
                                        chr16.append(".")

                                hexline = ('%08X' % self.address) + ": "
                                hexline += " ".join(str16) + "  "
                                hexline += ''.join(chr16)

                                buff_8k.append(hexline)
                                # 数据缓存到8k
                                # self.appendHexMsg(hexline)

                                # (8192/16)=512
                                if len(buff_8k) >= 512:
                                    if self.address == writeFileLength:
                                        self.appendHexMsg("超大文件不显示 请使用读芯片->文件功能保存数据")
                                    if self.address < writeFileLength:
                                        # 正常输出
                                        self.appendHexMsg("\n".join(buff_8k))
                                        buff_8k.clear()
                                    elif self.address & 0xffff == 0:
                                        self.appendMsg('%08X' % self.address)


                                hex16 = []
                                str16 = []
                                chr16 = []
                                self.address += 16
                                # if self.address % 0x2000 == 0 and self.address != (self.endAddress + 1) and self.address < writeFileLength:
                                #     # 按照8k数据显示横线
                                #     self.appendHexMsg("-" * 53)

                                progress = int(self.address * 100 / self.endAddress)
                                if progress > progress_temp:
                                    self.progressBarChange(progress)
                                    progress_temp = progress

                        # 保存文件 刷新
                        f.flush()

                # self.appendHexMsg('\n'.join(hex8K))
                endTime = time.time()

                # 查空结果
                if self.mode == 'ff':
                    if notFFcount > 0:
                        self.appendMsg("查空结果: {} 个非 FF 数据".format(notFFcount))
                    else:
                        self.appendMsg("查空正常")

                self.appendMsg(str("读取耗时: {}".format('%.2f' % (endTime - startTime))))
            else:
                self.appendMsg("编程器连接异常")

            eeprom.close()
        except Exception as e:
            print("错误")
            print(e)
            traceback.print_exc()

    def save(self):

        if os.path.exists(self.file) and os.path.samefile(self.file,temp_file):
            print("缓存文件和写入文件是同一个文件")
            self.appendMsg("缓存文件和写入文件是同一个文件")
            return
        else:
            self.appendMsg("缓存文件 {}".format(temp_file))
            self.appendMsg("另存文件 {}".format(self.file))
            # 复制文件
            shutil.copyfile(temp_file,self.file)

        # 移除原有数据写入文件的方法
        """
        if self.chipData:
            try:
                print("数据长度:{}".format(len(self.chipData)))
                print("写入文件:{}".format(self.file))
                with open(self.file, 'wb') as f:
                    for b in self.chipData:
                        f.write(struct.pack("B", b))
                        f.flush()
                self.appendMsg("保存文件完成")
            except Exception as e:
                print("错误")
                print(e)
                traceback.print_exc()
                # QMessageBox.warning(None, "错误", "文件写入失败", QMessageBox.Yes)

        else:
            QMessageBox.warning(None, "错误", "数据异常", QMessageBox.Yes)
        """

    # 先读取 再对比
    def readAndCheck(self):
        self.read()
        self.check()

    # 校验数据一致性
    def check(self):

        self.appendMsg("校验数据")

        if not self.file:
            self.appendMsg("没有选择校验文件")
            return

        isSame = True
        errorCount = 0
        buffer = []

        # 选择文件大小
        dataSize = os.path.getsize(self.file)


        # 使用缓存文件对比
        self.appendMsg("使用本地缓存文件校验数据 {}".format(temp_file))

        if not os.path.exists(temp_file):
            print("本地缓存文件不存在 退出")
            return

        # 缓存文件大小
        temp_dataSize = os.path.getsize(self.file)

        self.appendMsg("文件长度: {}".format(dataSize))
        self.appendMsg("缓存长度: {}".format(temp_dataSize))

        max_size = max(dataSize, temp_dataSize)

        try:
            with open(self.file, 'rb') as f1:
                with open(temp_file, 'rb') as f2:
                    # 循环对比
                    for i in range(max_size):
                        read1 = f1.read(1)
                        read2 = f2.read(1)

                        # 数据不一致
                        if read1 != read2:
                            isSame = False
                            errorCount += 1

                            if errorCount > 100:
                                continue

                            buffer.append("{}: {} {}".format(
                                '%08X' % i,
                                '%02s' % hex(read1[0])[2:].upper(),
                                '   %02s' % hex(read2[0])[2:].upper()))


        except Exception as e:
            print("错误")
            print(e)
            traceback.print_exc()

        # 暂时注释原有数据对比方式
        """
        data = self.chipData


        if len(data) == 0:
            # self.appendMsg("读取数据长度为0")
            # return
            self.read()
            data = self.chipData

        if os.path.getsize(self.file) != len(data):
            self.appendMsg("读取数据长度与文件不一致")
            self.appendMsg("文件长度: {}".format(os.path.getsize(self.file)))
            self.appendMsg("数据长度: {}".format(len(data)))
            # return

        try:
            with open(self.file, 'rb') as f:
                count = 0
                for b in data:
                    if count + 1 > dataSize:
                        break
                    readb = f.read(1)
                    if len(readb) > 0 and readb[0] != b:
                        isSame = False
                        errorCount += 1

                        if errorCount > 100:
                            continue

                        buffer.append("{}: {} {}".format(
                            '%08X' % count,
                            '%02s' % hex(readb[0])[2:].upper(),
                            '   %02s' % hex(b)[2:].upper()))
                    count += 1
                    if count % 64 == 0:
                        self.progressBarChange(int(count * 100 / len(data)))
        except Exception as e:
            print("错误")
            print(e)
            traceback.print_exc()
        """


        # 校验结果
        self.appendMsg("校验结果: {}".format("一致" if isSame else "不一致"))
        if not isSame:
            self.appendMsg("地址      文件  ROM")
            self.appendMsg('\n'.join(buffer))
            self.appendMsg("地址      文件  ROM")
            precent_ = (dataSize - errorCount) * 100 / dataSize
            print("precent: {}".format(precent_))
            if precent_ > 99.9:
                self.appendMsg("异常 一致率 : {}%".format('99.99'))
            else:
                self.appendMsg("异常 一致率 : {}%".format('%.2f' % precent_))


        self.appendMsg("校验结束")


class MQt(QtWidgets.QMainWindow, Ui_MainWindow):

    def __init__(self):
        super(MQt, self).__init__()
        self.setupUi(self)

        # 固定宽高
        self.setFixedSize(main_W, main_H)

        # 设置图标
        if os.path.exists('ico.ico'):
            self.setWindowIcon(QIcon('ico.ico'))
        else:
            icon = QPixmap()
            icon.loadFromData(iconB)
            self.setWindowIcon(QIcon(icon))

        self.setWindowTitle(mainTitle)

        # 开串口连接
        self.com = QSerialPort()

        self.updateButton.setEnabled(True)
        # 获取串口连接列表
        self.reload_ports()
        # 刷新
        self.updateButton.clicked.connect(self.reload_ports)
        # 列表项选中
        self.portList.itemClicked.connect(self.portList_itemClicked)
        # 点击连接按钮
        self.connectButton.clicked.connect(self.connectButton_clicked)
        self.readButton.clicked.connect(self.readButton_clicked)

        # 清除日志和hex日志
        self.clearButton.clicked.connect(lambda: (self.textBrowser.clear() or self.hexBrowser.clear()))

        # 文件 -> rom
        self.writeChipButton.clicked.connect(self.writeChipButton_clicked)
        # rom -> 文件
        self.writeFileButton.clicked.connect(self.writeFileButton_clicked)
        # 查文件和芯片内容
        self.checkFileButton.clicked.connect(self.checkFileButton_clicked)

        # 查ff
        self.verifyChipButton.clicked.connect(self.verifyChipButton_clicked)
        # 擦除
        self.eraserChipButton.clicked.connect(self.eraserChipButton_clicked)
        # 芯片信息
        self.chipInfoButton.clicked.connect(self.chipInfoButton_clicked)

        self.hexBrowser.setFontPointSize(8)

        self.mode = 0
        self.address = 0
        self.startAddress = 0
        self.selectFile = ''
        self.chipName = ''
        # 是否通过连接测试
        self.tested = False

        self.radios = [

            {'chip': self.radio32kMD1, 'name': '32', 'length': 0x7fff, 'buff': 128, 'page': 1, 'eraser': 1, 'mdmode': 1},

            {'chip': self.radio1MD1, 'name': '11', 'length': 0xfffff, 'buff': 128, 'page': 1, 'eraser': 1, 'mdmode': 1},
            {'chip': self.radio2MD1, 'name': '21', 'length': 0x1fffff, 'buff': 128, 'page': 1, 'eraser': 1, 'mdmode': 1},
            {'chip': self.radio3MD1, 'name': '31', 'length': 0x2fffff, 'buff': 128, 'page': 1, 'eraser': 1, 'mdmode': 1},
            {'chip': self.radio4MD1, 'name': '41', 'length': 0x3fffff, 'buff': 128, 'page': 1, 'eraser': 1, 'mdmode': 1},

            {'chip': self.radio32k, 'name': '11',  'length': 0x7fff,    'buff': 128, 'page': 1, 'eraser': 1, 'mdmode': 0},
            {'chip': self.radio512k,'name': '11',  'length': 0x7ffff,   'buff': 128, 'page': 1, 'eraser': 1, 'mdmode': 0},
            {'chip': self.radio1,   'name': '11',  'length': 0xfffff,   'buff': 128, 'page': 1, 'eraser': 1, 'mdmode': 0},
            {'chip': self.radio2,   'name': '21',  'length': 0x1fffff,  'buff': 128, 'page': 1, 'eraser': 1, 'mdmode': 0},
            {'chip': self.radio3,   'name': '31',  'length': 0x2fffff,  'buff': 128, 'page': 1, 'eraser': 1, 'mdmode': 0},
            {'chip': self.radio4,   'name': '41',  'length': 0x3fffff,  'buff': 128, 'page': 1, 'eraser': 1, 'mdmode': 0},
            {'chip': self.radio8,   'name': '81',  'length': 0x7fffff,  'buff': 128, 'page': 1, 'eraser': 1, 'mdmode': 0},
            {'chip': self.radio16,  'name': '161', 'length': 0xffffff,  'buff': 128, 'page': 1, 'eraser': 1, 'mdmode': 0},
            {'chip': self.radio32,  'name': '321', 'length': 0x1ffffff, 'buff': 128, 'page': 1, 'eraser': 1, 'mdmode': 0},

            {'chip': self.radio1M128, 'name': '1128', 'length': 0xfffff, 'buff': 128, 'page': 128, 'eraser': 1, 'mdmode': 0},
            {'chip': self.radio2M128, 'name': '2128', 'length': 0x1fffff, 'buff': 128, 'page': 128, 'eraser': 1, 'mdmode': 0},
            {'chip': self.radio3M128, 'name': '3128', 'length': 0x2fffff, 'buff': 128, 'page': 128, 'eraser': 1, 'mdmode': 0},
            {'chip': self.radio4M128, 'name': '4128', 'length': 0x3fffff, 'buff': 128, 'page': 128, 'eraser': 1, 'mdmode': 0},
            {'chip': self.radio4M128, 'name': '8128', 'length': 0x7fffff, 'buff': 128, 'page': 128, 'eraser': 1, 'mdmode': 0},
        ]

        # 循环增加点击事件
        for cp in self.radios:
            cp['chip'].clicked.connect(self.changeChip)

        for cp in self.radios:
            cp['chip'].setDisabled(True)

    # 刷新com口列表
    def reload_ports(self):
        self.portList.clear()
        self.connectButton.setEnabled(False)
        ports = QSerialPortInfo.availablePorts()
        print(ports)
        for port in ports:
            qitem = QListWidgetItem()
            if port.isBusy():
                qitem.setText(port.portName() + " (Busy)")
                qitem.setFlags(qitem.flags() & Qt.ItemIsUserCheckable)
            else:
                qitem.setText("{} {}".format(port.portName(), port.description()))
                qitem.setData(Qt.UserRole, port.portName())
                print(port.description())

            self.portList.addItem(qitem)

    # 选择com列表中的数据
    def portList_itemClicked(self):
        item = self.portList.currentItem()
        if item and item.flags():
            self.port = self.portList.currentItem().data(Qt.UserRole)
            self.connectButton.setEnabled(True)
            self.updateButton.setEnabled(True)

    def enableChip(self):
        self.verifyChipButton.setEnabled(True)
        self.eraserChipButton.setEnabled(True)
        self.chipInfoButton.setEnabled(True)
        # 读取按钮
        self.readButton.setEnabled(True)
        # 校验文件按钮
        self.checkFileButton.setEnabled(True)
        # 开启导入导出按钮
        self.writeChipButton.setEnabled(True)
        self.writeFileButton.setEnabled(True)

    def disableChip(self):
        self.verifyChipButton.setEnabled(False)
        self.eraserChipButton.setEnabled(False)
        self.chipInfoButton.setEnabled(False)
        # 读取按钮
        self.readButton.setEnabled(False)
        # 校验文件按钮
        self.checkFileButton.setEnabled(False)
        # 开启导入导出按钮
        self.writeChipButton.setEnabled(False)
        self.writeFileButton.setEnabled(False)

    # 点击connect连接按钮
    def connectButton_clicked(self):
        print("选择端口:{}".format(self.port))
        eeprom = Programmer()
        testRe = eeprom.testConnect(self.port)
        if testRe > 0:
            self.appendMsg("测试连接 {} 正常".format(self.port))
            self.enableChip()
            self.tested = True
            for cp in self.radios:
                cp['chip'].setDisabled(False)

            # 读取版本信息 更新到标题栏
            version = eeprom.getversion()
            dprint("version:{}".format(version))
            self.setWindowTitle("{} - ({})".format(mainTitle, version))

            # 选中最后一个
            # self.radios[len(self.radios) - 1]['chip'].click()
            # self.radio29040.click()
            self.radios[0]['chip'].click()

        else:
            # 错误返回小于0的值
            self.disableChip()
            self.tested = False
            for cp in self.radios:
                cp['chip'].setDisabled(True)
            if testRe == -9999:
                self.appendMsg("客户端版本与编程器不匹配")
                QMessageBox.warning(None, "错误", "客户端版本与编程器不匹配", QMessageBox.Yes)
            else:
                QMessageBox.warning(None, "错误", "连接COM失败,或未准备好", QMessageBox.Yes)

    def doChipThread(self):
        readChipThread = ChipThread(self.port, self.chip, self.chipName, self.startAddress, self.endAddress, self.mode,
                                    self.selectFile)
        # 连接信号槽
        readChipThread.browserClear_signal.connect(self.browserClear)
        readChipThread.hexBrowserAppend_signal.connect(self.hexBrowserAppend)
        readChipThread.textBrowserAppend_signal.connect(self.appendMsg)
        readChipThread.progressBarChange_signal.connect(self.progressBarChange)
        readChipThread.start()
        readChipThread.exec()

    def readButton_clicked(self):
        self.init()

        print("readButton_clicked")
        self.hexBrowser.clear();

        self.mode = 'r'
        self.appendMsg("读取开始")
        self.doChipThread()

    # 写入芯片 读文件
    def writeChipButton_clicked(self):
        self.init()

        fileName, fileType = QtWidgets.QFileDialog.getOpenFileName(self, "选择bin文件", os.getcwd(),
                                                                   "Bin Files(*.md;*.smd;*.bin);;All Files(*)")

        if not fileName:
            self.appendMsg("未选择文件")
            return

        # if not os.path.isfile(fileName) or os.path.getsize(fileName) > 0x80000:
        #     self.appendMsg("文件异常或体积大于512K")
        #     return

        self.selectFile = fileName
        self.appendMsg("读取文件: {}".format(fileName))

        # 设置写入模式
        self.mode = 'w'
        self.appendMsg("写入开始")
        self.doChipThread()

    # 读芯片 写文件
    def writeFileButton_clicked(self):
        self.init()
        fileName, fileType = QtWidgets.QFileDialog.getSaveFileName(self, "选择bin文件", os.getcwd(), "Bin Files(*.bin)")

        if not fileName:
            self.appendMsg("未选择文件")
            return

        self.selectFile = fileName
        self.appendMsg("输出文件: {}".format(fileName))

        # 不能选择缓存文件
        if os.path.exists(fileName) and os.path.samefile(fileName, temp_file):
            self.appendMsg(">> 请勿使用缓存文件作为输出文件 <<")
            self.appendMsg(">> 任务结束 <<")
            return

        # 读取并保存文件
        self.mode = 's'
        self.appendMsg("读取开始")
        self.doChipThread()

    # 校验内容
    def checkFileButton_clicked(self):
        self.init()

        fileName, fileType = QtWidgets.QFileDialog.getOpenFileName(self, "选择bin文件", os.getcwd(),
                                                                   "Bin Files(*.bin);;All Files(*)")
        if not fileName:
            self.appendMsg("未选择文件")
            return

        # if not os.path.exists(fileName) or not os.path.isfile(fileName) or os.path.getsize(fileName) > 0x80000:
        #     self.appendMsg("文件异常或体积大于521K")
        #     return
        #
        # if os.path.getsize(fileName) != self.chipLength:
        #     self.appendMsg("文件大小与芯片不一致")
        #     return

        self.selectFile = fileName

        # 不能选择缓存文件
        if os.path.samefile(fileName, temp_file):
            self.appendMsg(">> 缓存文件无法作为校验文件对比 <<")
            self.appendMsg(">> 任务结束 <<")
            return

        # 校验
        self.mode = 'c'
        self.appendMsg("读取并校验开始")
        self.doChipThread()

    # 查空 非ff
    def verifyChipButton_clicked(self):
        self.init()
        self.appendMsg("查空开始")
        self.mode = 'ff'
        self.doChipThread()

    # 擦0
    def eraserChipButton_clicked(self):
        self.init()

        reply = QMessageBox.question(self, '消息', '是否确认擦除?\n需要等待10秒后再进行其他操作',
                                     QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
        if reply != QMessageBox.Yes:
            print("取消擦除")
            return
        else:
            print("擦除开始")
            self.appendMsg("擦除开始")
            self.appendMsg("请5秒后在进行操作")
        eeprom = Programmer()
        if eeprom.connect(self.port):
            print("连接")
        else:
            print("未连接")
            self.appendMsg("==编程器未连接，或端口被占用==")
            return
        if eeprom.is_open:
            print("准备擦除")
            self.appendMsg("准备擦除")

            eeprom.sendCMD(CMD.Erase)

            w_re = eeprom.getline()
            if w_re:
                print("擦除返回值:{}".format(w_re))
                self.appendMsg("擦除返回:{}".format(w_re))
            else:
                print("没有返回值")

            print("擦除完成")
            self.appendMsg("擦除后建议使用查空进行检查")
        else:
            self.appendMsg("编程器连接异常")

    # 读取芯片信息
    def chipInfoButton_clicked(self):
        self.init()
        self.appendMsg("未设计此功能")
        # print("读取芯片信息开始")
        # self.appendMsg("读取芯片信息开始")
        # eeprom = Programmer()
        # if eeprom.connect(self.port):
        #     print("连接")
        # else:
        #     print("未连接")
        #     self.appendMsg("==编程器未连接，或端口被占用==")
        #     return
        # if eeprom.is_open:
        #
        #     self.address = 0
        #     eeprom.sendcommand("s",self.chipName)
        #     print("读取芯片信息完成")
        #     self.appendMsg("读取芯片信息完成")
        #     data = eeprom.read(2)
        #     if data:
        #         sips = []
        #         for d in data:
        #             sips.append(('%02X' % d))
        #         self.appendMsg("CHIP: {}".format(' '.join(sips)))
        # else:
        #     self.appendMsg("编程器连接异常")

    # ---------------------------------------

    def changeChip(self):
        for cp in self.radios:
            if cp['chip'].isChecked():
                self.chip = cp
                self.chipLength = cp['length'] + 1
                self.chipName = cp['name']
                self.endAddress = cp['length']

        # 启用
        self.writeChipButton.setDisabled(False)
        self.eraserChipButton.setDisabled(False)

        print("选择了{}".format(self.chip))

    def init(self):
        self.selectFile = ''
        self.mode = 'r'
        self.isHigh = 0
        self.address = 0

    # 进度条
    def progressBarChange(self, value):
        if not self.progressBar.isEnabled():
            self.progressBar.setEnabled(True)
        self.progressBar.setValue(int(value))

    # 清空
    def browserClear(self):
        self.hexBrowser.clear()

    # 追加
    def hexBrowserAppend(self, msg):
        self.hexBrowser.append(msg)

    # 消息框内容
    def appendMsg(self, msg):
        self.textBrowser.append(msg)


if __name__ == '__main__':
    try:
        app = QApplication(sys.argv)
        win = MQt()
        win.show()
        sys.exit(app.exec_())
    except Exception as e:
        print("错误")
        print(e)
        traceback.print_exc()